/*
 * DOMDocumentProxy.java
 *
 * Created on September 4, 2006, 11:44 AM
 *
 * (C) 2006 R. Alexander Milowski
 */

package org.infoset.xml.dom;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import org.infoset.xml.Attribute;
import org.infoset.xml.Characters;
import org.infoset.xml.Comment;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.infoset.xml.Item;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.Name;
import org.infoset.xml.Named;
import org.infoset.xml.ProcessingInstruction;
import org.infoset.xml.XMLException;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.DocumentType;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author alex
 */
public class DOMDocumentProxy extends DOMParentProxy implements org.w3c.dom.Document {

   static class ProxyDoctype extends DOMNodeProxy implements DocumentType {

      String qualifiedName;
      String publicId;
      String systemId;
      
      ProxyDoctype(DOMDocumentProxy proxy,String qualifiedName,String publicId,String systemId) {
         super(null,proxy);
         this.qualifiedName = qualifiedName;
         this.publicId = publicId;
         this.systemId = systemId;
      }
      
      public void setTextContent(String value) {
      }
      public String getTextContent() {
         return "";
      }
      public String getNodeValue() {
         return "";
      }
      public void setNodeValue(String value) {
      }
      public String getNodeName() {
         return getName();
      }
      public boolean hasAttributes() {
         return false;
      }
      public NamedNodeMap getAttributes() {
         return null;
      }
      public void setPrefix(String prefix) {
      }
      public String getPrefix() {
         return null;
      }
      public Node cloneNode(boolean deep) {
         return null;
      }
      public boolean hasChildNodes() {
         return false;
      }
      public NodeList getChildNodes() {
         return null;
      }
      public Node appendChild(Node n) {
         return null;
      }
      public Node removeChild(Node n) {
         return null;
      }
      public Node insertBefore(Node newChild,Node refChild) {
         return null;
      }
      public Node replaceChild(Node newChild,Node oldChild) {
         return null;
      }
      public Node getNextSibling() {
         return null;
      }
      public Node getPreviousSibling() {
         return null;
      }
      public Node getFirstChild() {
         return null;
      }
      public Node getLastChild() {
         return null;
      }
      public Node getParentNode() {
         return null;
      }
      public short getNodeType() {
         return Node.DOCUMENT_TYPE_NODE;
      }
      
      public NamedNodeMap getEntities() {
         return null;
      }
      public String getInternalSubset() {
         return "";
      }
      public String getName() {
         return qualifiedName;
      }
      public NamedNodeMap getNotations() {
         return null;
      }
      public String getPublicId() {
         return publicId;
      }
      public String getSystemId() {
         return systemId;
      }
   };
   class InfosetDOMImplementation implements DOMImplementation {
      public Object getFeature(String feature,String version) {
         return null;
      }
      public boolean hasFeature(String feature,String version) {
         return false;
      }
      public org.w3c.dom.Document createDocument(String namespaceURI, String qualifiedName, DocumentType doctype) {
         try {
            Document doc = itemConstructor.createDocument();
            String prefix = Name.getPrefix(qualifiedName);
            String localName = prefix.length()==0 ? qualifiedName : qualifiedName.substring(prefix.length()+1);
            doc.createDocumentElement(namespaceURI==null ? new Name(localName) : new Name(URI.create(namespaceURI),localName));
            doc.getDocumentElement().setPrefix(prefix);
            DOMDocumentProxy proxy = new DOMDocumentProxy(doc);
            proxy.doctype = new ProxyDoctype(proxy,doctype.getName(),doctype.getPublicId(),doctype.getSystemId());
            return proxy;
         } catch (XMLException ex) {
            throw new DOMException(DOMException.INVALID_STATE_ERR,ex.getMessage());
         }
      }
      
      public DocumentType createDocumentType(final String qualifiedName, final String publicId, final String systemId) {
         return new ProxyDoctype(DOMDocumentProxy.this,qualifiedName,publicId,systemId);
      }
   }
   protected ItemConstructor itemConstructor;
   protected Map<Item,Node> nodeMap;
   InfosetDOMImplementation implementation;
   DocumentType doctype;
   
   /** Creates a new instance of DOMDocumentProxy */
   public DOMDocumentProxy(Document doc) {
      super(doc,null);
      documentProxy = this;
      nodeMap = new HashMap<Item,Node>();
      nodeMap.put(doc,this);
      itemConstructor = doc.getInfoset().createItemConstructor(doc.getBaseURI());
      implementation = new InfosetDOMImplementation();
      doctype = null;
   }
   
   public Node getProxy(Item item) {
      if (item==null) {
         return null;
      }
      if (this.item==item) {
         return this;
      }
      if (this.item==item.getDocument()) {
         Node n = nodeMap.get(item);
         if (n==null) {
            n = newNode(item);
         }
         return n;
      } else {
         return null;
      }
   }
   
   protected Node newNode(Item c) {
      switch (c.getType()) {
         case ElementItem:
            return new DOMElementProxy((Element)c,this);
         case CharactersItem:
            return new DOMCharactersProxy((Characters)c,this);
         case CommentItem:
            return new DOMCommentProxy((Comment)c,this);
         case ProcessingInstructionItem:
            return new DOMProcessingInstructionProxy((ProcessingInstruction)c,this);
         case AttributeItem:
            return new DOMAttributeProxy((Attribute)c,this);
      }
      return null;
   }
   
   public DOMImplementation getImplementation() {
      return implementation;
   }
   
   public short getNodeType() {
      return Node.DOCUMENT_NODE;
   }
   
   public String getNodeValue() {
      return null;
   }
   
   public void setNodeValue(String value) {
   }
   
   public String getNodeName() {
      return "#document";
   }
   
   public org.w3c.dom.Element getDocumentElement() {
      return (org.w3c.dom.Element)getProxy(((Document)item).getDocumentElement());
   }
   
   public Node renameNode(Node n,String namespaceURI,String qualifiedName) {
      if (!(n instanceof DOMNodeProxy)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot renamed non-proxied nodes.");
      }
      Item theItem = ((DOMNodeProxy)n).item;
      int prefixLoc = qualifiedName.indexOf(':');
      String prefix = prefixLoc<0 ? Name.NO_PREFIX : qualifiedName.substring(0,prefixLoc);
      String localName = prefixLoc<0 ? qualifiedName : qualifiedName.substring(prefixLoc+1);
      Name nm = new Name(namespaceURI==null || namespaceURI.length()==0 ? Name.NO_NAMESPACE : URI.create(namespaceURI),localName);
      if (theItem instanceof Named) {
         Named named = (Named)n;
         named.setName(nm);
      }
      if (theItem instanceof Element) {
         ((Element)theItem).setPrefix(prefix);
      }
      if (theItem instanceof Attribute) {
         ((Element)theItem).setPrefix(prefix);
      }
      return n;
   }
   
   public org.w3c.dom.Element getElementById(String id)
   {
      Element e = ((Document)item).getElementWithId(id);
      return (org.w3c.dom.Element)getProxy(e);
   }
   
   public void setTextContent(String value) {
   }
   
   public String getTextContent() {
      return null;
   }
   
   public boolean hasAttributes() {
      return false;
   }
   
   public NamedNodeMap getAttributes() {
      return null;
   }
   
   public void setPrefix(String prefix) {
   }
   
   public String getPrefix() {
      return null;
   }
   
   public Node cloneNode(boolean deep) {
      return new DOMDocumentProxy((Document)item.copyOfItem(deep));
   }
   
   public Node getNextSibling() {
      return null;
   }
   
   public Node getPreviousSibling() {
      return null;
   }
   
   public void normalizeDocument() {
      throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Method not implemented.");
   }
   
   public DOMConfiguration getDomConfig() {
      throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Method not implemented.");
   }
   
   public Node adoptNode(Node n) {
      throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Method not implemented.");
   }
   
   public void setDocumentURI(String uri) {
      ((Document)item).setBaseURI(URI.create(uri));
   }

   public String getBaseURI() {
      return getDocumentURI();
   }
   
   public String getDocumentURI() {
      URI baseURI = ((Document)item).getBaseURI();
      System.err.println("Base URI = "+baseURI);
      return baseURI==null ? null : baseURI.toString();
   }
   
   public void setStrictErrorChecking(boolean flag) {
   }
   
   public boolean getStrictErrorChecking() {
      return false;
   }
   
   public void setXmlVersion(String value) {
      ((Document)item).setVersion(value);
   }
   
   public String getXmlVersion() {
      return ((Document)item).getVersion();
   }
   
   public void setXmlStandalone(boolean flag) {
      ((Document)item).setStandalone(flag);
   }
   
   public boolean getXmlStandalone() {
      return ((Document)item).isStandalone();
   }
   
   public void setXmlEncoding(String value) {
      ((Document)item).setEncoding(value);
   }
   
   public String getXmlEncoding() {
      return ((Document)item).getEncoding();
   }
   
   public String getInputEncoding() {
      return ((Document)item).getEncoding();
   }
   
   public org.w3c.dom.Attr createAttributeNS(String namespaceURI,String localName)
   {
      Attribute att = itemConstructor.createAttribute(new Name(URI.create(namespaceURI),localName),"");
      return (org.w3c.dom.Attr)getProxy(att);
   }
   
   public org.w3c.dom.Attr createAttribute(String localName)
   {
      Attribute att = itemConstructor.createAttribute(new Name(Name.NO_NAMESPACE,localName),"");
      return (org.w3c.dom.Attr)getProxy(att);
   }
   
   public org.w3c.dom.Element createElementNS(String namespaceURI,String localName) {
      try {
         Element e = itemConstructor.createElement(new Name(URI.create(namespaceURI),localName));
         return (org.w3c.dom.Element)getProxy(e);
      } catch (XMLException ex) {
         throw new DOMException(DOMException.INVALID_STATE_ERR,ex.getMessage());
      }
   }
   
   public org.w3c.dom.Element createElement(String localName) {
      try {
         Element e = itemConstructor.createElement(new Name(Name.NO_NAMESPACE,localName));
         return (org.w3c.dom.Element)getProxy(e);
      } catch (XMLException ex) {
         throw new DOMException(DOMException.INVALID_STATE_ERR,ex.getMessage());
      }
   }
   
   public Node importNode(Node n,boolean deep) {
      if (!(n instanceof DOMNodeProxy)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Importing nodes not backed by an infoset is not supported.");
      }
      DOMNodeProxy proxy = (DOMNodeProxy)n;
      Item copy = proxy.item.copyOfItem(deep);
      return getProxy(copy);
   }
   
   public EntityReference createEntityReference(String name) {
      throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Entity references are not supported.");
   }
   
   public org.w3c.dom.ProcessingInstruction createProcessingInstruction(String target,String data) {
      try {
         ProcessingInstruction pi = itemConstructor.createProcessingInstruction(new Name(target),data);
         return (org.w3c.dom.ProcessingInstruction)getProxy(pi);
      } catch (XMLException ex) {
         throw new DOMException(DOMException.INVALID_STATE_ERR,ex.getMessage());
      }
   }
   
   public org.w3c.dom.CDATASection createCDATASection(String data) {
      try {
         Characters cdata = itemConstructor.createCharacters(data);
         cdata.setCDataSection(true);
         return (org.w3c.dom.CDATASection)getProxy(cdata);
      } catch (XMLException ex) {
         throw new DOMException(DOMException.INVALID_STATE_ERR,ex.getMessage());
      }
   }
   
   public org.w3c.dom.Text createTextNode(String data) {
      try {
         Characters cdata = itemConstructor.createCharacters(data);
         return (org.w3c.dom.Text)getProxy(cdata);
      } catch (XMLException ex) {
         throw new DOMException(DOMException.INVALID_STATE_ERR,ex.getMessage());
      }
   }
   
   public org.w3c.dom.Comment createComment(String data) {
      try {
         Comment comment = itemConstructor.createComment(data);
         return (org.w3c.dom.Comment)getProxy(comment);
      } catch (XMLException ex) {
         throw new DOMException(DOMException.INVALID_STATE_ERR,ex.getMessage());
      }
   }
   
   public org.w3c.dom.DocumentFragment createDocumentFragment() {
      throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Document fragments not supported.");
   }

   public DocumentType getDoctype() {
      return doctype;
   }
}
